using System;
using System.Globalization;
using System.IO;
using System.Web;

using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (Pty) ltd
	Author:		Alan Benington	
	Started:	2005-05-15	
	Status:		release	
	Version:	2.0.0
	Buidl:		20070616
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/
	
/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20070524:	Modification for .Net 2.0
	20070616:	Addition of config object
	-----------------------------------------------------------------------	*/
	
namespace umlungu.engineX.webX {
	/// <summary>
	/// umlungu engineX web framework, page class.
	/// </summary>
	public class pageX : System.Web.UI.Page {
		#region Invisible properties
		private DateTime starttime;
		private siteX site;
		private browserX browser;
		private contentX content;
		private profileX profile;
		private XslCompiledTransform xsl;
		private XsltArgumentList xslParams;
		private bool wantEdit = false, wantTemplate = false;
		private bool isProfile, issecure, isform;
		private string movie_name, movie_elem;
		#endregion
		
		#region Constants
		#endregion
		
		#region Visible properties
		private bool debug = false;
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		protected bool Debug {
			get { return debug; }
			set { debug = value; }
		}
		#endregion
		
		#region Constructors/Destructors
		/// <summary>Constructor</summary>
		public pageX() {
			starttime = DateTime.Now;	// start timer
		}
		public pageX(bool wantdebug) {
			debug = wantdebug;
			starttime = DateTime.Now;	// start timer
		}
		public pageX(bool isedit, bool wanttmpl, bool wantdebug) {
			wantEdit = isedit;
			wantTemplate = wanttmpl;
			debug = wantdebug;
			starttime = DateTime.Now;	// start timer
		}
		#endregion
		
		#region Public methods
		/// <summary>
		/// Output a number of string parameters by Response.Write
		/// </summary>
		public void Initialise(string whichsite) {
			_Debug("Initialise", "site:", whichsite);
			try {
				site = new siteX(this, whichsite, debug);	// initialise default site object
				_Debug("Initialise", "after site");
				browser = new browserX(this);	// initialise browser object
				_Debug("Initialise", "after browser");
				isProfile = (site.ProfileTemplate != "");
				_Debug("Initialise", "isProfile:", isProfile.ToString());
				if (isProfile)
					profile = new profileX(this, debug, site.SiteID);
				_Debug("Initialise", "after profile");
				content = new contentX(this, debug, site, profile);	// initialise content object
				_Debug("Initialise", "after content");
				initialise();
			} catch (System.Exception e) {
				exceptionX exceptX = new exceptionX("Initialise", "error initialising page", e);
				throw(exceptX);
			}
		}
		/// <summary>
		/// Output a number of string parameters by Response.Write
		/// </summary>
		public void Output(params string[] outparam) {
			for (int i=0; i < outparam.Length; i++)
				Response.Write(outparam[i]);
		}
		#endregion

		#region Protected methods
		/// <summary>
		/// Method description
		/// </summary>
		protected void _Write() {
			if (content.Xml != null && xsl != null) {
				try {
					processPage();
					writeHead();
					writeParams();
					writeBody();
					killPage();
				} catch (System.Exception e) {
					exceptionX exceptX = new exceptionX("_Write", "error writing page", e);
					_Debug("_Write", exceptX.Xml.DocumentElement.OuterXml);
					//throw(exceptX);
				}
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		protected virtual void _WritePage() {
			_WriteContent();
		}
		/// <summary>
		/// Method description
		/// </summary>
		protected void _WriteError(System.Exception except) {
			Output("System error:: ", except.Message);
			if (debug) {
				Output("\ntrace: ", except.StackTrace);
				//serialiseAll();
			}
		}
		protected void _WriteError(exceptionX exceptX) {
			//Response.ContentType = "text/xml";
			Output(exceptX.Xml.OuterXml); 
			if (debug) {
				//serialiseAll();
			}
		}
		/// <summary>
		/// Method to do the content transform
		/// 	This is where the content from content xml pages is inserted...		
		/// 	NB The actual file is one of either:
		/// 	-	static content file determined by url parameters(xml)
		/// 	-	static content file determined by result of service request
		/// 	- 	content from web service (eg optimiser report)
		/// 	- 	error condition
		/// </summary>
		protected void _WriteContent() {
			if (xsl == null)
				throw(new exceptionX("_WriteContent", "content xsl not loaded"));
			if (content.Xml.DocumentElement != null)
				xsl.Transform(content.Xml, xslParams, Response.Output);
			// if the html is valid xml (including nbsp's) it will have been loaded ...but this accomodates when it is not
			else if (content.Type == "html") {
				string contxml = (string) Session["x_contxml"];
				Output(contxml);
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		protected void _WriteLinks(string appxsl) {
			string thisapp = String.Concat(site.SiteID, "_", appxsl);
			// relgloss :: if want the glossary in the related info column (not within content area) 
			_Debug("page._WriteLinks", "thisapp:", thisapp);
			// Load the XML links file and XSL menu teplate
		    XmlDocument linksxml = (XmlDocument) Application[site.LinksTemplate];
			if (linksxml == null)
				throw(new exceptionX("_WriteLinks", "links xml not loaded"));
			
			XslCompiledTransform linksxsl = (XslCompiledTransform) Application[thisapp];
			if (linksxsl == null)
				throw(new exceptionX("_WriteLinks", "links (", thisapp, ") xsl not loaded"));
			else
			    linksxsl.Transform(linksxml, xslParams, Response.Output);
		}
		/// <summary>
		/// Method to do the content transform for content managemnt
		///	This is where the content from content xml pages is managed...		
		///	NB This private void is after writeContent, except:
		///	- 	doesnt use content.RequireHtml
		/// </summary>
		private void _WriteManage() {
			if (content.Xml.DocumentElement != null)
			    xsl.Transform(content.Xml, xslParams, Response.Output);
		}
		/// <summary>
		/// Method description
		/// </summary>
		protected void _Debug(string label, params string[] outparam) {
			if (debug) {
				Response.Write("\n<!-- pageX.");
				Response.Write(label);
				Response.Write("::\n");
				for (int i=0; i < outparam.Length; i++)
					Response.Write(outparam[i]);
				Response.Write("\n");
				Response.Write(getElapsed());
				Response.Write("-->\n");
			}
		}
		#endregion

		#region Private methods
		/// <summary>
		/// Method description
		/// </summary>
		private void initialise() {
			_Debug("initialise");
			// initialise default properties
			movie_name = "";
			movie_elem = "";
			issecure = false;
			isform = false;
			doSecureRedir(site.WantLive);
			_Debug("initialise", "content template:", site.ContentTemplate);
			xsl = (XslCompiledTransform) Application[site.ContentTemplate];
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void killPage() {
			site.KillSite();
			if (debug)
				Output("\n<!-- time:: ", getElapsed(), "ms -->");
		}
		/// <summary>
		/// Method description
		/// </summary>
		private string getElapsed() {
			DateTime thistime = DateTime.Now;
			TimeSpan tspan = new TimeSpan(thistime.Ticks - starttime.Ticks);
			return(tspan.TotalMilliseconds.ToString());
		}
		/// <summary>
		/// Method description
		/// </summary>
		private string getLink() { return(content.Linkref); }
		
		private void doSecureRedir(bool want) {
			// ensure that once logged on the home page is set (and session is secure)
			if (want) {
				if(profile.Token != "") {
					if (Request.ServerVariables["HTTPS"] == "off")
						Response.Redirect(String.Concat(site.SecureUrl, site.Base, "?", Request.QueryString));
				}
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void writeHead() {
			string thisserv = (site.WantLive && issecure)? site.SecureUrl : site.Url;
			if (browser.IsStandard)	
				Output("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
			else // leaving out the url gets ie6 to run in 'quirks' (or 'backward compatibiltiy') mode
				Output("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n");
			if (wantTemplate)
				Output("<!-- ", content.Name, content.Process, ".html -->\n\n");
			Output("<html><head><title>");
			//	write site-page title:
			Output(site.Name, " ", site.VerMaj, ".", site.VerMin, " :: ", content.Title);
			Output(" :: ", profile.Group);
			Output("</title>");
			
			writeMeta();
			writeScripts(thisserv);
			writeStyles(thisserv);
			
			Output("\n</head>");
		}
		/// <summary>
		/// writeMeta:: write meta tags for search engines
		/// </summary>
		private void writeMeta() {
			if (site.IsMetatags) {
				XmlElement thiselem;
				string thisattr, thiscont;
				XmlNodeList thisnodeList = site.SiteXml.SelectNodes("metatags/metatag");
				foreach (XmlNode thisnode in thisnodeList) {
					thiselem = (XmlElement) thisnode;
					thisattr = thiselem.GetAttribute("name");
					thiscont = thiselem.GetAttribute("content");
					XmlElement pgnode = (XmlElement) content.Xml.DocumentElement.SelectSingleNode(String.Concat("//metatags/metatag[@name='", thisattr, "']"));
					if (pgnode != null) {
						if (thisattr == "keywords")
							thiscont = String.Concat(thiscont, ", ", pgnode.GetAttribute("content"));
						else
							thiscont = pgnode.GetAttribute("content");
						Output("\n<META NAME='", thisattr, "' CONTENT='", thiscont, "'/>");
					}
					else
						Output("\n<META NAME='", thisattr, "' CONTENT='", thiscont, "'/>");
				}
			}
		}
		/// <summary>
		/// writeScripts:: write site-page javascript resources
		/// </summary>
		private void writeScripts(string thisserv) {
			if(!wantTemplate) {
				if(site.SiteScript)
					Output("\n<script language='JavaScript' src='", thisserv, site.ScriptsDir, site.SiteID , ".js' type='text/javascript'></script>");
				else
					Output("\n<script language='JavaScript'>var x_debug = ", (debug)? "true" : "false", ";</script>");
				Output("\n<script language='JavaScript' src='", thisserv , site.ScriptsDir, "x_all.js' type='text/javascript'></script>");
				foreach (XmlNode scrpt in site.Scripts) {
					XmlElement scrptel = (XmlElement) scrpt;
					Output("\n<script language='JavaScript' src='", thisserv , site.ScriptsDir, scrptel.GetAttribute("name"), ".js' type='text/javascript'></script>");
				}
				// write the menu javascript resources
				writeMenu(true);
			}
		}
		/// <summary>
		/// writeStyles:: write the css files
		/// </summary>
		private void writeStyles(string thisserv) {
			if(wantTemplate)
				Output("\n<link rel='stylesheet' type='text/css' href='../../../templates/x_styles.css'/>");
			else
				Output("\n<link rel='stylesheet' type='text/css' href='", thisserv , site.StylesDir, "x_styles.css'/>");
			if (site.WantBrowsers && !browser.IE6)
				Output("\n<link rel='stylesheet' type='text/css' href='", thisserv , site.StylesDir, "x_styles_", browser.BrowserCode, ".css'/>");
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void writeBody() {
			string preloadstr = (site.Preload != "")? String.Concat("onload=\"x_preloadImages(", site.Preload, "); \"") : "";
			Output("<body ", preloadstr, " id='pagebody' ", site.GetBodyAttribs(), ">");
			writeSearch();
			logPage();
			_WritePage();
			writeMenu(false);
			writeMovie();
			Output("</body></html>");
		}
		/// <summary>
		/// This private void writes the page title and page intro elements. This is done:
		/// -	so that search engines can index and best prioritise
		/// -	by using the <h1> and <p> tags (these are reserved in the framework)
		/// -	tags are not visible, because they are defined as such in the css.		*/
		/// </summary>
		private void writeSearch() {
			if (site.WantSearch) {
				XmlNode thisnode = content.Xml.DocumentElement.SelectSingleNode("//page/search/title");
				string thispgtitle = (thisnode != null) ? thisnode.InnerText : "";
				thisnode = content.Xml.DocumentElement.SelectSingleNode("//page/search/intro");
				string thispgintro = (thisnode != null) ? thisnode.InnerText : "";
				// Write the page title and intro to h1 and p tags resectively
				Output("<h1>", thispgtitle, "</h1><p>", thispgintro, "</p>");
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void writeMenu(bool ishead) {
			if (site.WantDrop) {
				string edpar = (wantEdit) ? "&edit=yes" : "";
				if (ishead) {
					Output("\n<script language='JavaScript' src='", site.Url, "x_menus.js' type='text/javascript'></script>");
					Output("\n<script language='JavaScript' src='", site.Url, "menus.asp?link=", content.Linkref, edpar, "' type='text/javascript'></script>");
					Output("\n<script language='JavaScript' src='", site.Url, "x_menus_", browser.BrowserString, ".js' type='text/javascript'></script>");
				} else {
					Output("\n<script language='JavaScript' src='", site.Url, "x_loader.js' type='text/javascript'></script>");
				}
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		/*
		//If this browser understands the mimeTypes property and recognizes the MIME Type //"application/x-shockwave-flash"...
		if (navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"]){
		   //...write out the following <EMBED> tag into the document.
		   document.write('<EMBED SRC="flash_movie.spl" WIDTH="220" HEIGHT="110" LOOP="true" QUALITY="high">');
		}
		else {
		   //...write out the following <IMG> tag into the document. The image need
		   //not be the same size as the Flash Player movie, but it may help you lay out the
		   //page if you can predict the size of the object reliably.
		   document.write('<IMG SRC="welcome.gif" WIDTH="220" HEIGHT="110" ALT="Non-Shockwave Welcome">');
		}
		*/
		private void writeMovie() {
			string thismov = getMovie();
			if (thismov != "") {
				Output("\n<script language='JavaScript'>");
				Output("\nprivate void x_playFlash(thiselid, thisflash) {");
				Output("\n\tvar thisel = (HM_DOM)? document.getElementById(thiselid): eval('window.', thiselid);");
				Output("\n\tthisel.innerHTML = \"", thismov, "\";");
				Output("\n}");
				Output("\n</script>\n");
				Output("\n<script language='JavaScript'>");
				Output("\n\tx_playFlash('", movie_elem, "', '", movie_name, "'); ");
				Output("\n</script>");
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private string getMovie() {
			if (movie_name != "") {
				string thisret = "";
				if (site.GetFlash(movie_name) != null) {
					string cd = site.GetFlashAttr("classid");
					string ce = site.GetFlashAttr("codebase");
					string te = site.GetFlashAttr("type");
					string pe = site.GetFlashAttr("pluginspace");
					string wh = site.GetMovieAttr("width", movie_name);
					string ht = site.GetMovieAttr("height", movie_name);
					string sc = site.GetMovieAttr("src", movie_name);
					string lp = site.GetMovieAttr("loop", movie_name);
					string qy = site.GetMovieAttr("quality", movie_name);
					string se = site.GetMovieAttr("scale", movie_name);
					string sn = site.GetMovieAttr("salign", movie_name);
					string br = site.GetMovieAttr("bgcolor", movie_name);
					thisret = String.Concat("<OBJECT classid='", cd, "' codebase='", ce, "' WIDTH='", wh, "' HEIGHT='", ht, "'><PARAM NAME='movie' VALUE='", sc, "'/><PARAM NAME='loop' VALUE='", lp, "'/><PARAM NAME='quality' VALUE='", qy, "'/><PARAM NAME='scale' VALUE='", se, "'/><PARAM NAME='salign' VALUE='", sn, "'/><PARAM NAME='bgcolor' VALUE='", br, "'/><EMBED src='", sc, "' loop='", lp, "' quality='", qy, "' scale='", sc, "' salign='", sn, "' bgcolor='", br, "' WIDTH='", wh, "' HEIGHT='", ht, "' TYPE='", te, "' PLUGINSPAGE='", pe, "'></EMBED></OBJECT>");
				}
				return(thisret);
			}
			else
				return("");
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void logPage() {
			if (site.WantLogs) {
				XmlElement paramelem;
				DateTime now = DateTime.Now;
				string thisurlstr = site.Logs.SelectSingleNode("url").InnerText;
				bool want_server = (site.Logs.GetAttribute("server") == "yes") ? true : false;
				bool want_client = (site.Logs.GetAttribute("client") == "yes") ? true : false;
				
				if (want_client)
					thisurlstr = String.Concat(thisurlstr, "' + Math.random() + '");
				int i = 0;
				foreach (XmlNode paramnode in site.Logs.SelectNodes("param")) {
					i++;
					paramelem = (XmlElement) paramnode;
					thisurlstr = String.Concat(thisurlstr, (i==0)? "?" : "&", paramelem.GetAttribute("name"), "=", paramelem.InnerText);
				}
				thisurlstr = String.Concat(thisurlstr, "&sctn=", content.Section);
				thisurlstr = String.Concat(thisurlstr, "&subs=", content.Subsect);
				thisurlstr = String.Concat(thisurlstr, "&proc=", content.Process);
				thisurlstr = String.Concat(thisurlstr, "&page=", content.Name);
				thisurlstr = String.Concat(thisurlstr, "&sess=", Session.SessionID);
				thisurlstr = String.Concat(thisurlstr, "&link=", content.Linkref);
				thisurlstr = String.Concat(thisurlstr, "&time=", now.ToString());
				if (want_server) {
				/* do this as a web service... later.
					try {
						var serverXmlDoc = Server.CreateObject("MSXML2.ServerXMLHTTP");
						serverXmlDoc.open("GET", thisurlstr, false);
						serverXmlDoc.send(); 
						if (serverXmlDoc.status == 200)	// success
							retstr = "<div class='debug'>::callClickstream:(200)" + serverXmlDoc.responseText + "</div>";
						else {
							if (serverXmlDoc.status == 404)
								retstr = "<div class='debug'>::callClickstream:(404)::Clickstream not available</div>";
							else
								retstr = "<div class='debug'>::callClickstream:(" + serverXmlDoc.status + ")::Clickstream HTTP error</div>";
							}
						}
					catch (e) {
						retstr = "<div class='debug'>::callClickstream:(100)" + e.description + "</div>";
			    		}
				*/
				}
				if (want_client)
					Output("<img src='", thisurlstr, "' border='0'/>");
			}
		}
		/// <summary>
		/// This is where the content is processed (inserted later)
		/// NB Content is one of either:
		/// -	static content file determined by url parameters(xml)
		/// -	static content file determined by result of service request
		/// - 	content from web service (eg optimiser report)
		/// - 	error condition
		/// </summary>
		private void processPage() {
			content.FromUrl();
			_Debug("processPage", "Type:", content.Type);
			//	This is where server side form processing is done...
			if (content.Type == "form")
				getForm();
			//	This is where the web service is requested...
			if (content.Type == "service")
				getService();
			//	This is where external html is requested...
			if (content.Type == "html")
				getExthtml();
			// now load the content into a XML object...
			content.Load();
			processContent();
			content.Save();	// saves a debug file if debugging on
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void getForm() {
			string thisurl = String.Concat("handlers/forms/", content.Name, ".aspx");
			_Debug("getForm", "thisurl:", thisurl);
			Server.ScriptTimeout = site.TimeoutForm;
			getHandler(thisurl);
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void getService() {
			string thisurl = String.Concat("handlers/services/", content.Name, ".aspx");
			_Debug("getService", "thisurl:", thisurl);
			Server.ScriptTimeout = site.TimeoutServ;
			getHandler(thisurl);
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void getExthtml() {
			string thisurl = String.Concat("handlers/services/", content.Name, ".aspx");
			_Debug("getExthtml", "thisurl:", thisurl);
			getHandler(thisurl);
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void getHandler(string thisurl) {
			_Debug("getHandler", "thisurl:", thisurl);
			try {
				content.ToSession();
				Server.Execute(thisurl);
				content.FromSession();
			} catch (System.Exception e) {
				exceptionX exceptX = new exceptionX("getHandler", "error executing handler", e);
				_Debug("getHandler", exceptX.Xml.DocumentElement.OuterXml);
				throw(exceptX);
			}
			_Debug("getHandler");
		}
		/// <summary>
		/// processContent:: Method to pre-process the content xml
		/// </summary>
		private void processContent() {
			_Debug("processContent");
			if (content.Xml.DocumentElement != null) {
				checkMovie();
				AddInclude();
				AddProfile();
				AddLinks(site.WantAllLinks);
				AddData();
				// AddNMData();	// data-related elements (specific to NM) - later...
				AddGloss();
				AddRelinfo();
				AddMenuitems(site.WantAllLinks);
				// now check if form template handling required...	
				isform = checkLoadForm(true);
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private bool checkGloss() {
			XmlNode gloss = content.Xml.DocumentElement.SelectSingleNode("//glossary");
			return (gloss != null);
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void checkMovie() {
			XmlElement movelem = (XmlElement) content.Xml.DocumentElement.SelectSingleNode("/content/movie");
			if (movelem != null) {
				movie_name = movelem.GetAttribute("name");
				movie_elem = movelem.GetAttribute("id");
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void AddInclude() {
			XmlNodeList inclist = content.Xml.DocumentElement.SelectNodes("//include");
			XmlDocument contxml = new XmlDocument();
			XmlElement pginc;
			foreach (XmlNode inc in inclist) {
				pginc = (XmlElement) inc;
				string thisincr = pginc.GetAttribute("fldincr");
				string thisrept = pginc.GetAttribute("repeat");
				string thisfile = pginc.GetAttribute("file");
				string thiswhat = pginc.GetAttribute("what");
				if (thisfile != "") {
					// load the content file
					contxml.Load(Server.MapPath(thisfile));
					if (thisrept != "") {
						profileX thisprof = new profileX(this, false, site.SiteID);	// not quite sure if this should not be using the local profile
						thisrept = thisprof.GetProfileValue(thisrept);
					}
					
					if (thisincr == "yes") {
						XmlNodeList fldnodes = contxml.DocumentElement.SelectNodes("//fld");
						XmlElement fldelem;
						int i = 0;
						foreach (XmlNode fldnode in fldnodes) {
							i++;
							fldelem = (XmlElement) fldnode;
							fldelem.SetAttribute("name", String.Concat(fldelem.GetAttribute("name"), "_", i.ToString()));
						}
						fldnodes = contxml.DocumentElement.SelectNodes("//replace[@type='incr']");
						i = 0;
						foreach (XmlNode fldnode in fldnodes) {
							i++;
							fldelem = (XmlElement) fldnode.ParentNode;
							fldelem.SetAttribute("replace", i.ToString());
						}
					}
					
					// locate info nodes 
					XmlNode contnode = contxml.DocumentElement.SelectSingleNode(String.Concat("//", thiswhat));
					XmlNode parnode = pginc.ParentNode;
					if (contnode != null) {
						int nrept = (thisrept == "")? 1 : Convert.ToInt32(thisrept);	// do at least once
						for(int k=0; k<nrept; k++) {
							XmlNodeList incnodelist = contnode.ChildNodes;
							foreach (XmlNode incnode in incnodelist) {
								XmlElement newnode = xmlX.CloneElement(content.Xml, incnode);
								if (newnode != null) {
									if (newnode.Name == "fld")
										newnode.SetAttribute("name", String.Concat(newnode.GetAttribute("name"), (nrept==1)? "" : String.Concat("_", (k+1).ToString())));
									parnode.AppendChild(newnode);
								}
							}
						}
					}
				}
			}
			// now remove the include tags to clean up...
			xmlX.RemoveAll(inclist);
		}
		/// <summary>
		/// Method to add a node from the profile
		///		defined in the xml as: <profile name="nodename"/>
		/// </summary>
		private bool AddProfile() {
			_Debug("AddProfile", "isProfile", isProfile.ToString());
			if (isProfile) {
				string profname;
				XmlElement elem, profelem;
				XmlNodeList nodeList = content.Xml.DocumentElement.SelectNodes("//profile");
				foreach (XmlNode node in nodeList) {
					elem = (XmlElement) node;
					profname = elem.GetAttribute("name");
					_Debug("AddProfile", "profname", profname);
					bool want_par = (elem.GetAttribute("children_only") == "yes")? false : true;
					profelem = (XmlElement) profile.Xmldoc.SelectSingleNode(profname);
					if (profelem != null) {
						XmlNode parelem = elem.ParentNode;
						if (want_par)
							parelem.AppendChild(xmlX.CloneElement(content.Xml, profelem));
						else
							foreach (XmlNode child in profelem.ChildNodes)
								parelem.AppendChild(xmlX.CloneElement(content.Xml, child));
					}
				}
				xmlX.RemoveAll(nodeList);
				return(true);
			}
			return(false);
		}
		/// <summary>
		/// Method to add links to the xml (from the links already in application memory)
		/// </summary>
		private void AddLinks(bool want_all){
			XmlDocument linksxml = (XmlDocument) Application[site.LinksTemplate];
			if (want_all) {
				XmlNode contentnode = content.Xml.DocumentElement.SelectSingleNode("//content");
				XmlNode sitenode = linksxml.DocumentElement.SelectSingleNode("//website");
				contentnode.AppendChild(xmlX.CloneElement(content.Xml, sitenode));
			} else {
				XmlNode linknode, thisnode, urlnode;
				XmlElement pglnkelem;
				// Loop and add nodes
				foreach (XmlNode pglnknode in content.Xml.DocumentElement.SelectNodes("//lnk")) {
					pglnkelem = (XmlElement) pglnknode;
					// locate appropriate node 
					linknode = linksxml.DocumentElement.SelectSingleNode(String.Concat("//lnk[@name = '", pglnkelem.GetAttribute("name"), "']"));
					if (linknode != null) {
						// if the 'prompt' node does not exist (and this is a related link), add it
						thisnode = linknode.SelectSingleNode("prompt");
						if (thisnode != null) {
							if (pglnknode.SelectSingleNode("prompt") == null && pglnknode.ParentNode.Name == "links")
								pglnknode.AppendChild(xmlX.CloneElement(content.Xml, linknode));
						}
						// now add all the node Attributes
						foreach (XmlAttribute lnkattr in linknode.Attributes) {
							if (lnkattr.Name != "name")
								pglnkelem.SetAttribute(lnkattr.Name, lnkattr.Value);
						}
						// add the alturl node if it exists
						// if the 'prompt' node does not exist (and this is a related link), add it
						urlnode = linknode.SelectSingleNode("alturl");
						if (urlnode != null)
							pglnknode.AppendChild(xmlX.CloneElement(content.Xml, urlnode));
					}
				}
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void AddData() {
			if (isProfile) {
				// Add data from profile (within the session, if open)
				XmlNode datanode = content.Xml.DocumentElement.SelectSingleNode("//dataprofile");
				if (datanode != null) {
					AddDataProfile(datanode);
				}
				// Add sort order attribute for grpform elements
				datanode = content.Xml.DocumentElement.SelectSingleNode("//grpform");
				if (datanode != null) {
					AddSortOrder(datanode);
					AddFilterValue(datanode);
				}
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void AddDataProfile(XmlNode node){
			XmlElement elem = (XmlElement) node;
			// Locate node of correct type in profile data
			XmlNode thisnode = profile.Xmldoc.SelectSingleNode(String.Concat("//", elem.GetAttribute("type")));
			if (thisnode != null)
				node.AppendChild(xmlX.CloneElement(content.Xml, thisnode));
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void AddSortOrder(XmlNode grpfrmnode) {
			string thissort = Request.Form["SortOrder"];
			_Debug("AddSortOrder", "SortOrder", thissort);
			if (thissort != null)
				((XmlElement)grpfrmnode).SetAttribute("sortorder", thissort);
		}
		/// <summary>
		/// Method AddFilterValue: Add value element to the filter elements in a grpform
		/// </summary>
		private void AddFilterValue(XmlNode grpfrmnode) {
			// Loop through filter's
			string filtvalue;
			XmlElement filtelem;
			foreach (XmlNode filtnode in grpfrmnode.SelectNodes("//filter")) {
				filtelem = (XmlElement) filtnode;
				filtvalue = profile.GetProfileValue(filtelem.GetAttribute("name"));
				if (filtvalue != "") 
					xmlX.AddElem(content.Xml, filtnode, "value", filtvalue);
			}
			/* not necassary as the filter value is carried in the actual filter?...
			string filtxsl = ((XmlElement)grpfrmnode).GetAttribute("xslfilter");
			if (filtxsl != "") {
				string thisfilt = Request.Form["FilterVal"];
				_Debug("AddFilterValue", "FilterVal", thisfilt);
				if (thisfilt != null) {
					//string filtval = profile.GetProfileValue(thisfilt);
					((XmlElement)grpfrmnode).SetAttribute("filterval", thisfilt);
				}
			}
			*/
		}
		/// <summary>
		/// Method to add glossary items to the xml (from the items already in application memory)
		/// </summary>
		private void AddGloss() {
			XmlDocument glossxml = (XmlDocument) Application[site.GlossTemplate];
			XmlNode thisnode;
			if (glossxml != null) {
				foreach (XmlNode pgitemnode in content.Xml.DocumentElement.SelectNodes("//content/glossary/item")) {
					XmlElement pgitemelem = (XmlElement) pgitemnode;
					// locate appropriate node 
					XmlNode linknode = glossxml.DocumentElement.SelectSingleNode(String.Concat("//item[@name = '", pgitemelem.GetAttribute("name"), "']"));
					if (linknode != null){
						// add the term node
						thisnode = linknode.SelectSingleNode("term");
						if (thisnode != null)
							pgitemnode.AppendChild(xmlX.CloneElement(content.Xml, thisnode));
						// add the explain node if it exists
						thisnode = linknode.SelectSingleNode("explain");
						if (thisnode != null)
							pgitemnode.AppendChild(xmlX.CloneElement(content.Xml, thisnode));
					}
				}
			}
		}
		/// <summary>
		/// Method to add related info items to the xml (from the items already in application memory)
		/// </summary>
		private void AddRelinfo() {
			_Debug("AddRelinfo", "RequireRelinfo:", content.RequireRelinfo.ToString());
			if (content.RequireRelinfo) {
				XmlDocument linksxml = (XmlDocument) Application[site.LinksTemplate];
				// locate info nodes 
				XmlNode relnode = linksxml.DocumentElement.SelectSingleNode("//relinfo");
				if (relnode != null){
					relnode = content.Content.AppendChild(xmlX.CloneElement(content.Xml, relnode));
					XmlElement infelem;
					foreach (XmlNode infnode in relnode.SelectNodes("//info")) {
						infelem = (XmlElement) infnode;
						// locate appropriate node 
						XmlNode linknode = linksxml.DocumentElement.SelectSingleNode(String.Concat("//lnk[@name = '", infelem.GetAttribute("lnk"), "']"));
						if (linknode != null)
							infnode.AppendChild(xmlX.CloneElement(content.Xml, linknode));
					}
				}
			}
		}
		/// <summary>
		/// Method to add menu items to the xml for things like sitemap
		/// </summary>
		private void AddMenuitems(bool want_all) {
			if (!want_all) {	// if want_all, menus already included
				XmlNode pgmenunode = content.Xml.DocumentElement.SelectSingleNode("//menus");
				if (pgmenunode != null) {
					XmlDocument linksxml = (XmlDocument) Application[site.LinksTemplate];
					XmlNode menunode = linksxml.DocumentElement.SelectSingleNode("//menus");
					if (menunode != null){
						foreach (XmlNode topnode in menunode.SelectNodes("topmenu")) {
							add_menuitems(linksxml, topnode);
							pgmenunode.AppendChild(xmlX.CloneElement(content.Xml, topnode));
						}
					}
				}
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void add_menuitems(XmlDocument thislink, XmlNode thisnode) {
			XmlElement mnuelem;
			foreach (XmlNode mnunode in thisnode.SelectNodes("menuitem")) {
				mnuelem = (XmlElement) mnunode;
				// locate appropriate node 
				XmlNode linknode = thislink.DocumentElement.SelectSingleNode(String.Concat("//lnk[@name = '", mnuelem.GetAttribute("lnk"), "']"));
				if (linknode != null)
					mnunode.AppendChild(xmlX.CloneElement(content.Xml, linknode));
				// recursively call this to get all menuitem levels
				add_menuitems(thislink, mnunode);
			}
		}
		/// <summary>
		/// Method description
		///	Read and parse the xml and xsl form template files...	
		///	Create the XML for transformation by:					
		///		adding nodes from the wf_objs to the form XML	
		///		adding the data (pre-population) to the form XML	
		/// </summary>
		private bool checkLoadForm(bool prepop) {
			_Debug("checkLoadForm", "prepop:", prepop.ToString());
			XmlNode frm = content.Xml.DocumentElement.SelectSingleNode("/contentX/content/page/section[fields or confirms or wizard]");
			if (frm == null)
				return false;
			AddObjs();
			AddRef();
			if (prepop) 
				AddFormData();
			if (debug)
				content.Xml.Save(Server.MapPath(content.FilePath + "_dbg.xml"));
			return true;
		}
		/// <summary>
		/// AddObjs:: Add nodes from the wf_objs to the form XML
		/// </summary>
		private void AddObjs() {
			XmlDocument frmobjxml = (XmlDocument) Application[site.FormsTemplate];
			XmlElement thisfldelem;
			XmlNode objnode;
			// Loop and add nodes
			int i = 0;
			foreach (XmlNode thisfldnode in content.Xml.DocumentElement.SelectNodes("//fld")) {
				i++;
				thisfldelem = (XmlElement) thisfldnode;
				// locate appropriate wf_obj node and fill the child node with the wf_objs
				objnode = frmobjxml.DocumentElement.SelectSingleNode(String.Concat("//objects/wf_obj[@type = '", thisfldelem.GetAttribute("wf_obj"), "']"));
				if (objnode != null){
					foreach (XmlNode thisnode in objnode.ChildNodes) {
						// if <confirms> parent, skip some nodes
						if (thisfldnode.ParentNode.Name == "confirms") {
							if (thisnode.Name == "hintvalid" || thisnode.Name == "hintnormal" || thisnode.Name == "valid" )
								continue;
						}
						// if this node does not exists on the XML form node add it
						// special case: can have multiple subobjs (with different names)
						if (thisnode.Name == "subobjs") {
							XmlElement subel = (XmlElement) thisnode;
							string select = thisnode.Name;
							string subname = subel.GetAttribute("name");
							if (subname != "")
								select = String.Concat(thisnode.Name, "[@name='", subname,"']");
							if (thisfldnode.SelectSingleNode(select) == null)
								thisfldnode.AppendChild(xmlX.CloneElement(content.Xml, thisnode));
						} else if (thisfldnode.SelectSingleNode(thisnode.Name) == null)
							thisfldnode.AppendChild(xmlX.CloneElement(content.Xml, thisnode));
					}
				}
				// Add the id attribute
				thisfldelem.SetAttribute("id", (i+1).ToString());
				if (thisfldelem.GetAttribute("name") == "")
					thisfldelem.SetAttribute("name", String.Concat("wffld_", (i+1).ToString()));
			}
		}
		/// <summary>
		/// AddRef:: Replace the "ref" nodes with the correct value form the links
		/// </summary>
		private void AddRef() {
			XmlDocument linksxml = (XmlDocument) Application[site.LinksTemplate];
			XmlElement fieldnode = (XmlElement) content.Xml.DocumentElement.SelectSingleNode("//fields[@target!='']");
			if (fieldnode != null) {
				if (fieldnode.GetAttribute("target") != "") {
					XmlNode urlnode = linksxml.DocumentElement.SelectSingleNode(String.Concat("//lnk[@name='", fieldnode.GetAttribute("target"), "']/alturl[@type='file']"));
					if (urlnode != null) 
						fieldnode.SetAttribute("target", urlnode.InnerText);
				}
			}
		}
		/// <summary>
		/// AddFormData:: Add the data (pre-population) to the form XML from the Session profile.
		/// </summary>
		private void AddFormData() {
			if (isProfile) {
				_Debug("AddFormData");
				XmlNode objnode, thisnode;
				XmlElement thisfldelem;
				foreach (XmlNode thisfldnode in content.Xml.DocumentElement.SelectNodes("//fld")) {
					thisfldelem = (XmlElement) thisfldnode;
					// locate appropriate profile data node 
					objnode = profile.Xmldoc.SelectSingleNode(String.Concat("//fields/fld[@name = '", thisfldelem.GetAttribute("name"), "']/value"));
					// pre-populate the relevant field in the source
					if (objnode != null){
						thisnode = thisfldnode.SelectSingleNode("value");
						if (thisnode != null)
							if (((XmlElement)thisnode).GetAttribute("force") != "yes")
								thisnode.InnerText = objnode.InnerText;
						// add subobjs from profile
						XmlNode subobjnode = objnode.ParentNode.SelectSingleNode("subobjs");
						if (subobjnode != null) {
							XmlElement fldsubobjnode = (XmlElement) thisfldnode.SelectSingleNode("subobjs");
							if (fldsubobjnode == null) {
								fldsubobjnode = content.Xml.CreateElement("subobjs");
								thisfldnode.AppendChild(fldsubobjnode);
							}
							fldsubobjnode.InnerXml = subobjnode.InnerXml;
						}
					}
				}
			}
		}	
		/// <summary>
		/// Method description
		/// </summary>
		private void serialiseAll() {
			serialiseSite();
			serialiseContent();
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void serialiseSite() {
			if (site != null) {
				XmlSerializer serial = new XmlSerializer(typeof(siteX));
				StreamWriter writer = new StreamWriter(Server.MapPath("x_siteX_dbg.xml"));
				serial.Serialize(writer, site);
			}
		}
		/// <summary>
		/// Method description
		/// </summary>
		private void serialiseContent() {
			if (content != null) {
				XmlSerializer serial = new XmlSerializer(typeof(contentX));
				StreamWriter writer = new StreamWriter(Server.MapPath("x_contentX_dbg.xml"));
				serial.Serialize(writer, content);
			}
		}	
		/// <summary>
		/// Method description
		/// </summary>
		private void writeParams() {
			_Debug("writeParams", "profile:", profile.Xml.OuterXml);
			xslParams = new XsltArgumentList();
			xslParams.AddParam("want_debug", "", debug? "yes" : "no");
			// Write content link as navigable xml node
			XPathNavigator linknav = content.Xml.CreateNavigator();
			XPathNodeIterator linkit = linknav.Select("//link");
			xslParams.AddParam("x_link", "", linkit);
			// Write site as navigable xml node
			XPathNavigator sitenav = site.Xml.CreateNavigator();
			XPathNodeIterator siteit = sitenav.Select("//site");
			xslParams.AddParam("x_site", "", siteit);
			// Write identity as navigable xml node
			if (isProfile) {
				XPathNavigator profnav = profile.Xml.CreateNavigator();
				XPathNodeIterator profit = profnav.Select("/profile");
				xslParams.AddParam("x_profile", "", profit);
			}
			// Parameters to control url paths
			xslParams.AddParam("x_baseurl", "", (wantEdit)? String.Concat(site.Edit, "?link=") : String.Concat(site.Base, "?link="));
			xslParams.AddParam("x_serverurl", "", (site.WantLive)? ((issecure) ? site.SecureUrl : site.Url) : site.Url);
			xslParams.AddParam("x_secureurl", "", site.SecureUrl);
			xslParams.AddParam("x_siteurl", "", site.SiteUrl);
			// Parameters to control content editing - needs revision
			xslParams.AddParam("want_edit", "", (Session["x_want_edit"] != null)? (string) Session["x_want_edit"] : "yes");
			xslParams.AddParam("edit_mode", "", (wantEdit)? "edit" : "");
			// Parameters to control template output
			xslParams.AddParam("want_template", "", (wantTemplate)? "yes" : "no");
			xslParams.AddParam("img_root", "", (wantTemplate)? "../../../templates/" : "images/");
		}
		#endregion
	}
}
